package elsim.scenarios

import junit.framework.Assert;
import java.util.Iterator;

import org.apache.log4j.BasicConfigurator;
import org.joda.time.DateTime
import org.junit.BeforeClass;
import org.junit.Test
import elsim.scenarios.ScenarioReader.EventType;
import elsim.common.ElSimConstants;
import elsim.controller.Controller;

/**
 * Default implementation of controller which allows to override 
 * only some methods selectively, which is good for testing.
 */
class DefaultMockController implements Controller {
  @Override
  public void onGlobalInit(int lifts, int floors, DateTime startTime) {
  }

  @Override
  public void onInitLift(int lift, int position) {
  }

  @Override
  public void onCall(String name, int from, int to, int clock) {
  }

  @Override
  public void onFail(int lift, int clock) {
  }

  @Override
  public void onFix(int lift, int clock) {
  }

  @Override
  public void onDisplay(int clock) {
  }

  @Override
  public void onStats(int clock) {
  }
}

class ScenarioReaderTest {
  @BeforeClass static void before() {
    BasicConfigurator.configure()
  }
  
  @Test void parseDateTime() {
    String time = "09:00:00"
    DateTime dt = ScenarioReader.parseDateTime(time)
    Assert.assertEquals(new DateTime(1970,1,1,9,0,0,0), dt)
  }

  @Test void initializeBasic() {
    def initLines = [
      "StartTime:\t09:00:00",
      "NumberOfFloors:\t5",
      "NumberOfElevators:\t3"
    ]

    boolean globalInitHappened = false
    ScenarioReader reader = new ScenarioReader(initLines, new DefaultMockController(){
          @Override
          public void onGlobalInit(int lifts, int floors, DateTime startTime) {
            Assert.assertEquals(5, floors)
            Assert.assertEquals(3, lifts)

            globalInitHappened = true
          }
        });

    reader.initialize(initLines.iterator())
    Assert.assertTrue(globalInitHappened)
  }

  @Test void initializeLifts() {
    def initLines = [
      "StartTime:\t09:00:00",
      "NumberOfFloors:\t5",
      "NumberOfElevators:\t3",
      "init\t0\t0",
      "init\t1\t0",
      "init\t2\t3"
    ]

    int callbackWatchdog = 0
    def expectedValues = [
      [0, 0],
      [1, 0],
      [2, 15]
    ]
    ScenarioReader reader = new ScenarioReader(initLines, new DefaultMockController(){
          @Override
          public void onInitLift(int lift, int position) {
            def expected = expectedValues[callbackWatchdog++]
            Assert.assertEquals(expected[0], lift)
            Assert.assertEquals(expected[1], position)
          }
        });

    def iterator = initLines.iterator()
    reader.initialize(iterator)
    reader.initLifts(iterator)
    Assert.assertTrue(callbackWatchdog == 3)
  }

  @Test void eventType() {
    def lines = [
      [
        "call whoever",
        EventType.Call
      ],
      [
        "fail whatever",
        EventType.Fail
      ],
      ["fix however", EventType.Fix],
      [
        "display whenever",
        EventType.Display
      ],
      [
        "stats whichever",
        EventType.Stats]
    ]

    for (def line : lines) {
      Assert.assertEquals(line[1], ScenarioReader.eventType(line[0]))
    }

    try {
      ScenarioReader.eventType("something")
    } catch (IllegalArgumentException e) {
      return
    }

    Assert.fail("No IllegalArgumentException was thrown")
  }

  @Test void dateTimeToTicks() {
    def initLines = [
      "StartTime:\t09:00:00",
      "NumberOfFloors:\t5",
      "NumberOfElevators:\t3"
    ]

    ScenarioReader reader = new ScenarioReader(initLines, new DefaultMockController());
    reader.initialize(initLines.iterator())

    def DateTime dt = reader.parseDateTime("09:00:05")
    Assert.assertEquals(5, reader.dateTimeToTicks(dt))
  }

  @Test void handleCall() {
    def initLines = [
      "StartTime:\t09:00:00",
      "NumberOfFloors:\t5",
      "NumberOfElevators:\t3",
      "init\t0\t0",
      "init\t1\t0",
      "init\t2\t3",
      "call\tsgzmd\t0\t09:05:00\t3"
    ]

    boolean callbackWatchdog = false
    ScenarioReader reader = new ScenarioReader(initLines, new DefaultMockController(){
          @Override
          public void onCall(String name, int from, int to, int clock) {
            callbackWatchdog = true
            Assert.assertEquals("sgzmd", name)
            Assert.assertEquals(0, from)
            Assert.assertEquals(15, to)
            Assert.assertEquals(5 * 60, clock)
          }
        });
    reader.readScenario()
    Assert.assertTrue(callbackWatchdog)
  }

  @Test void handleFailAndFix() {
    def initLines = [
      "StartTime:\t09:00:00",
      "NumberOfFloors:\t5",
      "NumberOfElevators:\t3",
      "init\t0\t0",
      "init\t1\t0",
      "init\t2\t3",
      "fail\t0\t09:05:00",
      "fix\t0\t09:06:00"
    ]

    int callbackWatchdog = 0
    ScenarioReader reader = new ScenarioReader(initLines, new DefaultMockController(){
          @Override
          public void onFail(int lift, int clock) {
            Assert.assertEquals(0, lift)
            Assert.assertEquals(5*60, clock)

            // this must be first call
            Assert.assertEquals(0, callbackWatchdog)
            ++callbackWatchdog
          }

          @Override
          public void onFix(int lift, int clock) {
            Assert.assertEquals(0, lift)
            Assert.assertEquals(6*60, clock)
            ++callbackWatchdog
          }
        });

    reader.readScenario()
    Assert.assertEquals(2, callbackWatchdog)
  }

  @Test void handleStatsAndDisplay() {
    def initLines = [
      "StartTime:\t09:00:00",
      "NumberOfFloors:\t5",
      "NumberOfElevators:\t3",
      "init\t0\t0",
      "init\t1\t0",
      "init\t2\t3",
      "stats\t09:05:00",
      "display\t09:06:00"
    ]

    int callbackWatchdog = 0
    ScenarioReader reader = new ScenarioReader(initLines, new DefaultMockController(){
          @Override
          public void onStats(int clock) {
            Assert.assertEquals(5*60, clock)

            // this must be first call
            Assert.assertEquals(0, callbackWatchdog)
            ++callbackWatchdog
          }

          @Override
          public void onDisplay(int clock) {
            Assert.assertEquals(6*60, clock)
            ++callbackWatchdog
          }
        });

    reader.readScenario()
    Assert.assertEquals(2, callbackWatchdog)
  }
}
